

import LocalMap from "../../cfw/local/LocalMap";
import { engine } from "../../engine/engine";
import { OperateName, WebParam } from "./WebDBDefine";
import { WebStatement } from "./WebStatement";
import { WebTable } from "./WebTable";
import { WebTableOperator } from "./WebTableOperator";
import { WebValueData } from "./WebValueData";
import { WebWhereData } from "./WebWhereData";
let sqlite_master = 'sqlite_master'
export class WebDB implements WebTableOperator {
    protected static dbBuffer: { [key: string]: WebDB } = {}

    static getDB(dbName: string): WebDB {
        let instance = this.dbBuffer[dbName]
        if (!instance) {
            instance = new WebDB(dbName)
            this.dbBuffer[dbName] = instance;
        } else {
            console.error('no db name is ', dbName)
        }
        return instance;
    }


    protected tableBuffer: { [key: string]: WebTable } = {}

    createTable(dbName: string, tableName: string, param: WebParam[]): WebTable {
        let instance: WebTable = this.tableBuffer[tableName]
        if (!instance) {
            instance = new WebTable()
            instance.init(this.data, dbName, tableName, param)
            this.tableBuffer[tableName] = instance;
        } else {
            console.error('no table name is ', tableName)
        }
        return instance;
    }

    getTable(tableName: string) {
        return this.tableBuffer[tableName]
    }


    hasTable(tableName: string) {
        return this.tableBuffer[tableName] != undefined;
    }

    protected data: LocalMap

    protected name: string;

    constructor(name: string) {
        this.name = name;
        if (!this.data) {
            this.data = new LocalMap(name, { head: {}, data: [] })
        }
        this.createSqliteMaster()
        let table = this.data.get(sqlite_master)
        if (table) {
            let list = table.data;
            for (let index = 0; index < list.length; index++) {
                const element = list[index];
                this.exec(element.sql)
            }
        }

        console.log(' tableBuffer ', this.tableBuffer)

    }

    protected createSqliteMaster() {
        var sqlStr = 'CREATE TABLE sqlite_master (name text primary key,type text,tbl_name text,sql text,rootpage integer)'
        this.exec(sqlStr, false)
    }

    protected addTableToMaster(tbName: string, sql: string, rootpage: number) {
        let table = this.data.get(sqlite_master)
        if (table) {
            let flag = true;
            let name = "'" + tbName + "'"
            for (let index = 0; index < table.data.length; index++) {
                const element = table.data[index];
                if (element.name == name) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                let data = { name: name, type: "'table'", tbl_name: name, sql: sql, rootpage: rootpage }
                table.data.push(data)
                this.data.update(sqlite_master, table)
            }
        }
    }

    protected deleteTableFromMaster(tbName: string) {
        let table = this.data.get(sqlite_master)
        if (table) {
            let name = "'" + tbName + "'"
            let list: any[] = table.data
            for (let index = 0; index < list.length; index++) {
                const element = list[index];
                if (element.name == name) {
                    list.splice(index, 1)
                    this.data.update(sqlite_master, table)
                    break;
                }
            }
        }
    }
    protected deleteTable(tbName: string) {
        let table = this.getTable(tbName)
        if (table) {
            table.delete()
            this.deleteTableFromMaster(tbName)
        }
    }




    exec(sqlStr: string, addToMaster: boolean = true) {
        // LogUtils.log(' exec sqlStr ', sqlStr)
        let result = sqlStr.match(/\s*create\s*table\s*(\b\D\w*\b)\s*\(([\s\S]*)\)/i)
        // console.log('exec result ', result)
        if (result) {

            if (!result[1]) {
                console.error('tableName is null ')
                throw 'tableName is null '
            }
            let tableName = result[1]
            if (this.hasTable(tableName)) {
                return;
            }
            // console.log('tableName ', tableName)
            let param = result[2]
            // console.log('param ', param)
            let p: WebParam[] = []
            if (param) {
                let list = param.split(',')
                // console.log('list ', list)
                for (let index = 0; index < list.length; index++) {
                    const element = list[index];
                    let r = element.match(/\s*(\b\D\w*\b)\s*(\b[a-z]*\b)\s*(primary\s*key)?.*/i)
                    // console.log('r ', r)
                    p.push({ key: r[1], type: r[2], attr: r[3] })
                }
            }
            if (addToMaster) {
                this.addTableToMaster(tableName, sqlStr, 2)
            }
            this.createTable(this.name, tableName, p)
            return;
        } else {
            result = sqlStr.match(/\s*drop\s*table\s*(\b\D\w*\b)\s*/i)
            // console.log('exec result ', result)
            if (result) {
                let tableName = result[1]
                this.deleteTable(tableName)
            } else {
                engine.error(' exc error ', sqlStr)
            }
        }


    }

    prepare(sqlStr: string): WebStatement {
        // LogUtils.log(" sqlStr ", sqlStr)
        if (sqlStr.match(/select/i)) {
            let result = sqlStr.match(/\s*(select)\s*(\*|\b\D\w*\b)\s*from\s*(\b\D\w*\b)\s*(?:where\s*([\s\S]*))?/i)
            // console.log('select result ', result)
            if (result) {
                let stat = new WebStatement(this)
                stat.operate = OperateName.select
                stat.valueData = new WebValueData(stat.operate, result[2])
                stat.tableName = result[3]
                stat.whereData = new WebWhereData(stat.operate, result[4])
                // console.log('select stat ', stat)
                return stat;
            } else {
                engine.error(' prepare select ', sqlStr, result)
                return null;
            }

        }

        if (sqlStr.match(/update/i)) {
            // let sqlStr = 'update ' + this.tbName + ' set value = ? where id=?';
            // var sqlStr = 'update ' + this.tbName + ' set value=@value where id=@id';
            let result = sqlStr.match(/\s*(update)\s*(\b\D\w*\b)\s*set\s*([\s\S]*)\s*where\s*([\s\S]*)/i)
            // console.log('update result 222', result)
            if (result) {
                let stat = new WebStatement(this)
                stat.operate = OperateName.update;
                stat.tableName = result[2]
                stat.valueData = new WebValueData(stat.operate, result[3])
                stat.whereData = new WebWhereData(stat.operate, result[4])
                // console.log('update stat ', stat)
                return stat;
            } else {
                engine.error(' prepare update ', sqlStr, result)
                return null;
            }


        }

        if (sqlStr.match(/insert/i)) {
            //(id,value,num) VALUES (@id, @value)'
            let result = sqlStr.match(/\s*(insert)\s*into\s*(\b\D\w*\b)\s*\(([\D\w]*)\)\s*VALUES\s*\(([\D\w]*)\)\s*/i)
            // console.log('insert result 333', result)
            if (result) {
                let stat = new WebStatement(this)
                stat.operate = OperateName.insert;
                stat.tableName = result[2]
                stat.valueData = new WebValueData(stat.operate, result[3])
                stat.whereData = new WebWhereData(stat.operate, result[4])


                // console.log('insert stat ', stat)
                return stat;
            } else {
                engine.error(' prepare insert ', sqlStr, result)
                return null;
            }

        }

        if (sqlStr.match(/delete/i)) {
            let result = sqlStr.match(/\s*delete\s*from\s*(\b\D\w*\b)\s*where\s*([\s\S]*)/i)
            // console.log('delete result 333', result)
            if (result) {
                let stat = new WebStatement(this)
                stat.operate = OperateName.delete
                stat.tableName = result[1]
                stat.whereData = new WebWhereData(stat.operate, result[2])
                // console.log('delete stat ', stat)
                return stat;
            } else {
                engine.error(' prepare delete ', sqlStr, result)
                return null;
            }

        }


        return null;
    }


    transaction(call: Function) {
        return (list: any[]) => {
            call(list)
        }
    }



}